﻿#include "osgqt.h"

#include <QKeyEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QThread>
#include <QApplication>
#include <QScreen>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QWindow>
#include <QMainWindow>
#include <QLayout>
#include <QInputDialog>
#include <QtMath>

#include <osgDB/ReadFile>
#include <osg/Plane>
#include <osg/LineWidth>


class HudCallback :public osg::NodeCallback
{
public:
    HudCallback(osgViewer::Viewer* viewer):m_viewer(viewer)
    {
    }
    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
    {

        osg::MatrixTransform* pTM = dynamic_cast<osg::MatrixTransform*>(node);
        if(pTM)
        {
            osg::Camera* camera = m_viewer->getCamera();
            osg::Vec3 translate = pTM->getMatrix().getTrans();
            osg::Vec3 scale = pTM->getMatrix().getScale();
            osg::Matrix mv = camera->getViewMatrix();

            osg::Matrix inv_mv = camera->getInverseViewMatrix();  // 将视图矩阵转换为正常的变换矩阵
            osg::Quat inv_q = inv_mv.getRotate();
            osg::Quat q = mv.getRotate();

            // 模型当前所处的旋转坐标系，和相机所处的坐标系存在绕x轴逆时顺时针转90度
            osg::Quat dq(osg::DegreesToRadians(90.0f),osg::Vec3(1.0f,0.0f,0.0f));
            // pTM->setMatrix(osg::Matrix::scale(scale)* osg::Matrix::rotate( dq * q ) * osg::Matrix::translate(translate));
            static osg::Matrix mm = osg::Matrix::rotate(dq);
            mv.setTrans(translate);
            pTM->setMatrix(osg::Matrix::scale(scale) *  mv /** mm*/);
        }
    }
private:
    osg::ref_ptr<osgViewer::Viewer> m_viewer;
};

namespace
{

class QtKeyboardMap
{
public:
    QtKeyboardMap()
    {
        mKeyMap[Qt::Key_Escape     ] = osgGA::GUIEventAdapter::KEY_Escape;
        mKeyMap[Qt::Key_Delete     ] = osgGA::GUIEventAdapter::KEY_Delete;
        mKeyMap[Qt::Key_Home       ] = osgGA::GUIEventAdapter::KEY_Home;
        mKeyMap[Qt::Key_Enter      ] = osgGA::GUIEventAdapter::KEY_KP_Enter;
        mKeyMap[Qt::Key_End        ] = osgGA::GUIEventAdapter::KEY_End;
        mKeyMap[Qt::Key_Return     ] = osgGA::GUIEventAdapter::KEY_Return;
        mKeyMap[Qt::Key_PageUp     ] = osgGA::GUIEventAdapter::KEY_Page_Up;
        mKeyMap[Qt::Key_PageDown   ] = osgGA::GUIEventAdapter::KEY_Page_Down;
        mKeyMap[Qt::Key_Left       ] = osgGA::GUIEventAdapter::KEY_Left;
        mKeyMap[Qt::Key_Right      ] = osgGA::GUIEventAdapter::KEY_Right;
        mKeyMap[Qt::Key_Up         ] = osgGA::GUIEventAdapter::KEY_Up;
        mKeyMap[Qt::Key_Down       ] = osgGA::GUIEventAdapter::KEY_Down;
        mKeyMap[Qt::Key_Backspace  ] = osgGA::GUIEventAdapter::KEY_BackSpace;
        mKeyMap[Qt::Key_Tab        ] = osgGA::GUIEventAdapter::KEY_Tab;
        mKeyMap[Qt::Key_Space      ] = osgGA::GUIEventAdapter::KEY_Space;
        mKeyMap[Qt::Key_Delete     ] = osgGA::GUIEventAdapter::KEY_Delete;
        mKeyMap[Qt::Key_Alt        ] = osgGA::GUIEventAdapter::KEY_Alt_L;
        mKeyMap[Qt::Key_Shift      ] = osgGA::GUIEventAdapter::KEY_Shift_L;
        mKeyMap[Qt::Key_Control    ] = osgGA::GUIEventAdapter::KEY_Control_L;
        mKeyMap[Qt::Key_Meta       ] = osgGA::GUIEventAdapter::KEY_Meta_L;
        mKeyMap[Qt::Key_F1             ] = osgGA::GUIEventAdapter::KEY_F1;
        mKeyMap[Qt::Key_F2             ] = osgGA::GUIEventAdapter::KEY_F2;
        mKeyMap[Qt::Key_F3             ] = osgGA::GUIEventAdapter::KEY_F3;
        mKeyMap[Qt::Key_F4             ] = osgGA::GUIEventAdapter::KEY_F4;
        mKeyMap[Qt::Key_F5             ] = osgGA::GUIEventAdapter::KEY_F5;
        mKeyMap[Qt::Key_F6             ] = osgGA::GUIEventAdapter::KEY_F6;
        mKeyMap[Qt::Key_F7             ] = osgGA::GUIEventAdapter::KEY_F7;
        mKeyMap[Qt::Key_F8             ] = osgGA::GUIEventAdapter::KEY_F8;
        mKeyMap[Qt::Key_F9             ] = osgGA::GUIEventAdapter::KEY_F9;
        mKeyMap[Qt::Key_F10            ] = osgGA::GUIEventAdapter::KEY_F10;
        mKeyMap[Qt::Key_F11            ] = osgGA::GUIEventAdapter::KEY_F11;
        mKeyMap[Qt::Key_F12            ] = osgGA::GUIEventAdapter::KEY_F12;
        mKeyMap[Qt::Key_F13            ] = osgGA::GUIEventAdapter::KEY_F13;
        mKeyMap[Qt::Key_F14            ] = osgGA::GUIEventAdapter::KEY_F14;
        mKeyMap[Qt::Key_F15            ] = osgGA::GUIEventAdapter::KEY_F15;
        mKeyMap[Qt::Key_F16            ] = osgGA::GUIEventAdapter::KEY_F16;
        mKeyMap[Qt::Key_F17            ] = osgGA::GUIEventAdapter::KEY_F17;
        mKeyMap[Qt::Key_F18            ] = osgGA::GUIEventAdapter::KEY_F18;
        mKeyMap[Qt::Key_F19            ] = osgGA::GUIEventAdapter::KEY_F19;
        mKeyMap[Qt::Key_F20            ] = osgGA::GUIEventAdapter::KEY_F20;
        mKeyMap[Qt::Key_hyphen         ] = '-';
        mKeyMap[Qt::Key_Equal         ] = '=';
        mKeyMap[Qt::Key_division      ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
        mKeyMap[Qt::Key_multiply      ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
        mKeyMap[Qt::Key_Minus         ] = '-';
        mKeyMap[Qt::Key_Plus          ] = '+';
        mKeyMap[Qt::Key_Insert        ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
    }

    ~QtKeyboardMap()
    {
    }

    int remapKey(QKeyEvent* event)
    {
        KeyMap::iterator itr = mKeyMap.find(event->key());

        if(itr == mKeyMap.end())
        {
            return int(*(event->text().toLatin1().data()));
        }
        else
            return itr->second;
    }

private:
    typedef std::map<unsigned int, int> KeyMap;
    KeyMap mKeyMap;
};

static QtKeyboardMap s_QtKeyboardMap;
}

#if (QT_VERSION < QT_VERSION_CHECK(5, 2, 0))
#define GETDEVICEPIXELRATIO() 1.0
#else
#define GETDEVICEPIXELRATIO() devicePixelRatio()
#endif


osgQOpenGLWidget::osgQOpenGLWidget(QWidget *parent):QOpenGLWidget(parent)
{
    //setMouseTracking(true);
    this->setFocusPolicy(Qt::StrongFocus);
}

osgQOpenGLWidget::osgQOpenGLWidget(osg::ArgumentParser *arguments, QWidget *parent):
    QOpenGLWidget(parent),
    _arguments(arguments)
{
    //setMouseTracking(true);
    this->setFocusPolicy(Qt::StrongFocus);
}

osgQOpenGLWidget::~osgQOpenGLWidget()
{

}

osgViewer::Viewer *osgQOpenGLWidget::getOsgViewer()
{
    return m_renderer;
}

OpenThreads::ReadWriteMutex *osgQOpenGLWidget::mutex()
{
    return &_osgMutex;
}

void osgQOpenGLWidget::showfloor(bool isshow)
{
    m_renderer->showground(isshow);
}

void osgQOpenGLWidget::Display(osg::ref_ptr<osg::Node> node)
{
    _containt.append(node);
    m_renderer->getroot()->addChild(node);    
    m_renderer->redarw();
}

void osgQOpenGLWidget::showobject(bool isshow, osg::ref_ptr<osg::Node> node)
{
    m_renderer->showobject(isshow,node);
}

void osgQOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    createRenderer();
    emit initialized();
}

void osgQOpenGLWidget::resizeGL(int w, int h)
{
    Q_ASSERT(m_renderer);

    QScreen* screen = (windowHandle() && windowHandle()->screen()) ? windowHandle()->screen() :
                          qApp->screens().front();
    m_renderer->resize(w, h, screen->devicePixelRatio());
}

void osgQOpenGLWidget::paintGL()
{
    OpenThreads::ScopedReadLock locker(_osgMutex);
    if (_isFirstFrame) {
        _isFirstFrame = false;
        m_renderer->getCamera()->getGraphicsContext()->setDefaultFboId(defaultFramebufferObject());
    }
    m_renderer->frame();
}

void osgQOpenGLWidget::setDefaultDisplaySettings()
{
    osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
    ds->setNvOptimusEnablement(1);
    ds->setStereo(false);
}

void osgQOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    Q_ASSERT(m_renderer);

    if(event->key() == Qt::Key_F)
    {
        static QSize g;
        static QMargins sMargins;

        if(parent() && parent()->isWidgetType())
        {
            QMainWindow* _mainwindow = dynamic_cast<QMainWindow*>(parent());

            if(_mainwindow)
            {
                g = size();

                if(layout())
                    sMargins = layout()->contentsMargins();

                bool ok = true;

                // select screen
                if(qApp->screens().size() > 1)
                {
                    QMap<QString, QScreen*> screens;
                    int screenNumber = 0;

                    for(QScreen* screen : qApp->screens())
                    {
                        QString name = screen->name();

                        if(name.isEmpty())
                        {
                            name = tr("Screen %1").arg(screenNumber);
                        }

                        name += " (" + QString::number(screen->size().width()) + "x" + QString::number(
                                    screen->size().height()) + ")";
                        screens[name] = screen;
                        ++screenNumber;
                    }

                    QString selected = QInputDialog::getItem(this,
                                                             tr("Choose fullscreen target screen"), tr("Screen"), screens.keys(), 0, false,
                                                             &ok);

                    if(ok && !selected.isEmpty())
                    {
                        context()->setScreen(screens[selected]);
                        move(screens[selected]->geometry().x(), screens[selected]->geometry().y());
                        resize(screens[selected]->geometry().width(),
                               screens[selected]->geometry().height());
                    }
                }

                if(ok)
                {
                    // in fullscreen mode, a thiner (1px) border around
                    // viewer widget
                    if(layout())
                        layout()->setContentsMargins(1, 1, 1, 1);

                    setParent(0);
                    showFullScreen();
                }
            }
        }
        else
        {
            showNormal();
            setMinimumSize(g);
            QMainWindow* _mainwindow = dynamic_cast<QMainWindow*>(parent());
            if(_mainwindow){
                _mainwindow->setCentralWidget(this);
            }

            if(layout())
                layout()->setContentsMargins(sMargins);

            qApp->processEvents();
            setMinimumSize(QSize(1, 1));
        }
    }
    else // not 'F' key
    {
        // forward event to renderer
        m_renderer->keyPressEvent(event);
    }
}

void osgQOpenGLWidget::keyReleaseEvent(QKeyEvent *event)
{
    Q_ASSERT(m_renderer);
    // forward event to renderer
    m_renderer->keyReleaseEvent(event);
}

void osgQOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
    //qDebug()<<"osgQOpenGLWidget::mousePressEvent";
    Q_ASSERT(m_renderer);
    // forward event to renderer
    m_renderer->mousePressEvent(event);
}

void osgQOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    Q_ASSERT(m_renderer);
    // forward event to renderer
    m_renderer->mouseReleaseEvent(event);
}

void osgQOpenGLWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_ASSERT(m_renderer);
    // forward event to renderer
    m_renderer->mouseDoubleClickEvent(event);
}

void osgQOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    Q_ASSERT(m_renderer);
    // forward event to renderer
    m_renderer->mouseMoveEvent(event);
}

void osgQOpenGLWidget::wheelEvent(QWheelEvent *event)
{
    Q_ASSERT(m_renderer);
    // forward event to renderer
    m_renderer->wheelEvent(event);
}

void osgQOpenGLWidget::createRenderer()
{
    setDefaultDisplaySettings();
    if (!_arguments) {
        m_renderer = new OSGRenderer(this);
    } else {
        m_renderer = new OSGRenderer(_arguments, this);
    }
    QScreen* screen = windowHandle()
                              && windowHandle()->screen() ? windowHandle()->screen() :
                          qApp->screens().front();
    m_renderer->setupOSG(width(), height(), screen->devicePixelRatio());
}

void osgQOpenGLWidget::scale(osg::MatrixTransform *mtx, float tarscale)
{
    mtx->setMatrix(osg::Matrix::scale(tarscale,tarscale,tarscale));
}

void osgQOpenGLWidget::rotation(osg::MatrixTransform *mtx, double angle, osg::Vec3d dir)
{
    mtx->setMatrix(osg::Matrix::rotate(osg::inDegrees(angle),dir));
}

void osgQOpenGLWidget::translate(osg::MatrixTransform *mtx, osg::Vec3d trans)
{
    mtx->setMatrix(osg::Matrix::translate(trans));
}

OSGRenderer::OSGRenderer(QObject *parent):QObject(parent),osgViewer::Viewer(),gridwidth(1000),gridheight(1000)
{

}

OSGRenderer::OSGRenderer(osg::ArgumentParser *arguments, QObject *parent):QObject(parent),osgViewer::Viewer(*arguments),gridwidth(1000),gridheight(1000)
{

}

OSGRenderer::~OSGRenderer()
{

}

void OSGRenderer::keyPressEvent(QKeyEvent *event)
{
    setKeyboardModifiers(event);
    int value = s_QtKeyboardMap.remapKey(event);
    m_osgWinEmb->getEventQueue()->keyPress(value);
}

void OSGRenderer::keyReleaseEvent(QKeyEvent *event)
{
    if(event->isAutoRepeat())
    {
        event->ignore();
    }
    else
    {
        setKeyboardModifiers(event);
        int value = s_QtKeyboardMap.remapKey(event);
        m_osgWinEmb->getEventQueue()->keyRelease(value,1);
    }
}

void OSGRenderer::mousePressEvent(QMouseEvent *event)
{
    int button = 0;
    //qDebug()<<"OSGRenderer::mousePressEvent"<<event->x()<<event->y();
    switch(event->button())
    {
    case Qt::LeftButton:
        button = 1;
        break;

    case Qt::MidButton:
        button = 2;
        break;

    case Qt::RightButton:
        button = 3;
        break;

    case Qt::NoButton:
        button = 0;
        break;

    default:
        button = 0;
        break;
    }
    setKeyboardModifiers(event);
    m_osgWinEmb->getEventQueue()->mouseButtonPress(event->x() * m_windowScale,
                                                   event->y() * m_windowScale, button);
}

void OSGRenderer::mouseReleaseEvent(QMouseEvent *event)
{
    int button = 0;

    switch(event->button())
    {
    case Qt::LeftButton:
        button = 1;
        break;

    case Qt::MidButton:
        button = 2;
        break;

    case Qt::RightButton:
        button = 3;
        break;

    case Qt::NoButton:
        button = 0;
        break;

    default:
        button = 0;
        break;
    }
    setKeyboardModifiers(event);
    m_osgWinEmb->getEventQueue()->mouseButtonRelease(event->x() * m_windowScale,
                                                     event->y() * m_windowScale, button);
}

void OSGRenderer::mouseDoubleClickEvent(QMouseEvent *event)
{
    int button = 0;

    switch(event->button())
    {
    case Qt::LeftButton:
        button = 1;
        break;

    case Qt::MidButton:
        button = 2;
        break;

    case Qt::RightButton:
        button = 3;
        break;

    case Qt::NoButton:
        button = 0;
        break;

    default:
        button = 0;
        break;
    }

    setKeyboardModifiers(event);
    m_osgWinEmb->getEventQueue()->mouseDoubleButtonPress(event->x() * m_windowScale,
                                                         event->y() * m_windowScale, button);
}

void OSGRenderer::mouseMoveEvent(QMouseEvent *event)
{
    //qDebug()<<"OSGRenderer::mouseMoveEvent"<<event->x()<<event->y();
    setKeyboardModifiers(event);
    m_osgWinEmb->getEventQueue()->mouseMotion(event->x() * m_windowScale,
                                              event->y() * m_windowScale);
}

void OSGRenderer::wheelEvent(QWheelEvent *event)
{
    setKeyboardModifiers(event);
    m_osgWinEmb->getEventQueue()->mouseMotion(event->x() * m_windowScale,
                                              event->y() * m_windowScale);
    m_osgWinEmb->getEventQueue()->mouseScroll(
        event->orientation() == Qt::Vertical ?
            (event->delta() > 0 ? osgGA::GUIEventAdapter::SCROLL_UP :
                 osgGA::GUIEventAdapter::SCROLL_DOWN) :
            (event->delta() > 0 ? osgGA::GUIEventAdapter::SCROLL_LEFT :
                 osgGA::GUIEventAdapter::SCROLL_RIGHT));
}

void OSGRenderer::resize(int windowWidth, int windowHeight, float windowScale)
{
    if(!m_osgInitialized)
        return;

    m_windowScale = windowScale;


    m_osgWinEmb->resized(0, 0,
                         windowWidth * windowScale,
                         windowHeight * windowScale);
    m_osgWinEmb->getEventQueue()->windowResize(0, 0,
                                               windowWidth * windowScale,
                                               windowHeight * windowScale);

    update();
}

void OSGRenderer::setupOSG(int windowWidth, int windowHeight, float windowScale)
{
    m_osgInitialized = true;
    m_windowScale = windowScale;
    m_osgWinEmb = new osgViewer::GraphicsWindowEmbedded(0, 0,
                                                        windowWidth * windowScale, windowHeight * windowScale);
    m_osgWinEmb->getEventQueue()->syncWindowRectangleWithGraphicsContext();
    _camera->setViewport(new osg::Viewport(0, 0, windowWidth * windowScale,
                                           windowHeight * windowScale));
    _camera->setGraphicsContext(m_osgWinEmb.get());

    setKeyEventSetsDone(0);
    setReleaseContextAtEndOfFrameHint(false);
    setThreadingModel(osgViewer::Viewer::SingleThreaded);

    osgViewer::Viewer::Windows windows;
    getWindows(windows);
    viewMatrix=new osg::MatrixTransform;
    viewMatrix->setMatrix(osg::Matrix::translate(osg::Vec3(0,0,-300))*osg::Matrix::rotate(osg::inDegrees(30.0f),osg::Vec3(1,0,0)));
    root=new osg::Group;
    viewMatrix->addChild(root);
    //root->addChild(viewMatrix);
    floor=createBase(osg::Vec3d(0.0,0.0,0.0),500);
    floor->setNodeMask(0);
    grid=createGrid(gridwidth,gridheight);
    root->addChild(grid);
    root->addChild(floor);
    HUDroot = new HUDAxis;
    osg::ref_ptr<osg::Node> nodeaxes= osgDB::readNodeFile("axes.osgt");
    HUDroot->addChild(nodeaxes);
    HUDroot->setMainCamera(getCamera());
    HUDroot->setRenderOrder(osg::Camera::POST_RENDER);
    HUDroot->setClearMask(GL_DEPTH_BUFFER_BIT);
    HUDroot->setAllowEventFocus(false);
    HUDroot->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    root->addChild(HUDroot);


    setSceneData(viewMatrix);
    _timerId = startTimer(10, Qt::PreciseTimer);
    _lastFrameStartTime.setStartTick(0);
}

void OSGRenderer::redarw()
{
    setSceneData(root);
}

bool OSGRenderer::checkNeedToDoFrame()
{
    // check if any event handler has prompted a redraw
    if(_requestRedraw)
        return true;

    if(_requestContinousUpdate)
        return true;

    // check if the view needs to update the scene graph
    // this check if camera has update callback and if scene requires to update scene graph
    if(requiresUpdateSceneGraph())
        return true;

    // check if the database pager needs to update the scene
    if(getDatabasePager()->requiresUpdateSceneGraph())
        return true;

    // check if the image pager needs to update the scene
    if(getImagePager()->requiresUpdateSceneGraph())
        return true;


    // check if the scene needs to be redrawn
    if(requiresRedraw())
        return true;

    // check if events are available and need processing
    if(checkEvents())
        return true;

    // and check again if any event handler has prompted a redraw
    if(_requestRedraw)
        return true;

    if(_requestContinousUpdate)
        return true;

    return false;
}

void OSGRenderer::frame(double simulationTime)
{
    // limit the frame rate
    if(getRunMaxFrameRate() > 0.0)
    {
        double dt = _lastFrameStartTime.time_s();
        double minFrameTime = 1.0 / getRunMaxFrameRate();

        if(dt < minFrameTime)
            QThread::usleep(static_cast<unsigned int>(1000000.0 * (minFrameTime - dt)));
    }

    // avoid excessive CPU loading when no frame is required in ON_DEMAND mode
    if(getRunFrameScheme() == osgViewer::ViewerBase::ON_DEMAND)
    {
        double dt = _lastFrameStartTime.time_s();

        if(dt < 0.01)
            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0 *
                                                                      (0.01 - dt)));
    }

    // record start frame time
    _lastFrameStartTime.setStartTick();
    // make frame

#if 1
    osgViewer::Viewer::frame(simulationTime);
#else

    if(_done) return;

    // OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl;

    if(_firstFrame)
    {
        viewerInit();

        if(!isRealized())
        {
            realize();
        }

        _firstFrame = false;
    }

    advance(simulationTime);

    eventTraversal();
    updateTraversal();
    //    renderingTraversals();
#endif
}

void OSGRenderer::requestRedraw()
{
    osgViewer::Viewer::requestRedraw();
}

bool OSGRenderer::checkEvents()
{
    for(Devices::iterator eitr = _eventSources.begin();
         eitr != _eventSources.end();
         ++eitr)
    {
        osgGA::Device* es = eitr->get();

        if(es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
        {
            if(es->checkEvents())
                return true;
        }

    }

    // get events from all windows attached to Viewer.
    Windows windows;
    getWindows(windows);

    for(Windows::iterator witr = windows.begin();
         witr != windows.end();
         ++witr)
    {
        if((*witr)->checkEvents())
            return true;
    }
    return false;
}

void OSGRenderer::update()
{
    osgQOpenGLWidget* osgWidget = dynamic_cast<osgQOpenGLWidget*>(parent());
    osgWidget->_osgWantsToRenderFrame = true;
    osgWidget->update();
}

void OSGRenderer::scale(osg::MatrixTransform *mtx, float tarscale)
{
    mtx->setMatrix(osg::Matrix::scale(tarscale,tarscale,tarscale));
}

void OSGRenderer::rotation(osg::MatrixTransform *mtx, double angle, osg::Vec3d dir)
{
    mtx->setMatrix(osg::Matrix::rotate(osg::inDegrees(angle),dir));
}

void OSGRenderer::translate(osg::MatrixTransform *mtx, osg::Vec3d trans)
{
    mtx->setMatrix(osg::Matrix::translate(trans));
}

osg::Camera *OSGRenderer::createHudCamera(Viewer *viewer, int width, int height)
{
    osg::ref_ptr<osg::Camera> hudCamera=new osg::Camera;
    hudCamera->setProjectionMatrixAsOrtho(0,width,0,height,1,100);
    hudCamera->setRenderOrder(osg::Camera::POST_RENDER);
    hudCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    hudCamera->setClearMask(GL_DEPTH_BUFFER_BIT);
    osg::Node* axes=osgDB::readNodeFile("axes.osgt");
    osg::MatrixTransform* ptm=new osg::MatrixTransform;
    ptm->addChild(axes);
    axes->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
    ptm->setMatrix(osg::Matrix::scale(osg::Vec3(width/12,width/12,width/12))*osg::Matrix::translate(osg::Vec3(width/20,width/20,1)));
    ptm->setUpdateCallback(new HudCallback(this));
    hudCamera->addChild(ptm);
    hudCamera->setViewMatrixAsLookAt(osg::Vec3(0,0,1),osg::Vec3(0,0,0),osg::Vec3(0,1,0));
    return hudCamera.release();
}

void OSGRenderer::addModel(osg::MatrixTransform *mtx,osg::Node* node)
{
    osg::MatrixTransform* modelmtx=new osg::MatrixTransform;
    osg::Matrixd mat1 = viewMatrix->getMatrix();
    osg::Matrixd mat2 = mtx->getMatrix();
    modelmtx->setMatrix(mat1*mat2);
    modelmtx->addChild(node);
    viewMatrix->addChild(modelmtx);
}

void OSGRenderer::showground(bool isshow)
{
    if(isshow)
    {
        int t = root->getChildIndex(floor);
        root->getChild(t)->setNodeMask(1);
    }
    else
    {
        int t = root->getChildIndex(floor);
        root->getChild(t)->setNodeMask(0);
    }
}

void OSGRenderer::showobject(bool isshow, osg::ref_ptr<osg::Node> node)
{
    int t=root->getChildIndex(node);
    qDebug()<<"children index == "<<t<<root->containsNode(node);
//    if(t<0||t>= root->getNumChildren())
//        return;
    osg::ref_ptr<osg::Node> nd;
    if(root->containsNode(node))
    {
        if(isshow)
        {
            root->getChild(t)->setNodeMask(1);
        }
        else
            root->getChild(t)->setNodeMask(0);
    }
    else
    {
        for(int i=0;i<root->getNumChildren();++i)
        {
            int num=root->getChild(i)->asGroup()->getNumChildrenRequiringUpdateTraversal();
            qDebug()<<"child model "<<i<<"num == "<<num;
        }
    }

}

void OSGRenderer::showgGrid(bool isshow)
{
    grid->setNodeMask(isshow?1:0);
}

void OSGRenderer::setGridSize(double width, double height)
{
    gridwidth=width;
    gridheight=height;
}

void OSGRenderer::createAxis(osg::Transform *previousJoint)
{
    double height = 12.0;
    double radius = .5;
    osg::TessellationHints* hints = new osg::TessellationHints;
    hints->setDetailRatio(0.5f);
    osg::MatrixTransform* zmt = new osg::MatrixTransform();

    previousJoint->addChild(zmt);
    osg::ShapeDrawable *zShape = new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(0.0f,0.0f,height/2),radius,height),hints);
    osg::ShapeDrawable *zCone = new osg::ShapeDrawable(new osg::Cone(osg::Vec3(0.0f,0.0f,1.0),radius+1.0,2.0),hints);

    osg::MatrixTransform* zmtCone = new osg::MatrixTransform();
    osg::Geode *zgCone = new osg::Geode;

    zmtCone->setMatrix( osg::Matrix::translate(0.0,0.0,height));
    previousJoint->addChild(zmtCone);

    zShape->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
    zCone->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
    osg::Geode *z = new osg::Geode;
    z->addDrawable(zShape);
    zgCone->addDrawable(zCone);
    zmtCone->addChild(zgCone);
    zmt->addChild(z);

    osg::MatrixTransform* mt = new osg::MatrixTransform();
    previousJoint->addChild(mt);

    osg::Matrix xMatrix = osg::Matrix::rotate(-osg::PI_2, 0.0, 1.0, 0.0);
    mt->setMatrix(xMatrix);


    osg::ShapeDrawable *xShape = new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(0.0f,0.0f,height/2),radius,height),hints);
    xShape->setColor(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
    osg::Geode *x = new osg::Geode;
    x->addDrawable(xShape);
    mt->addChild(x);


    osg::MatrixTransform *yMt = new osg::MatrixTransform();
    previousJoint->addChild(yMt);
    osg::Matrix yMatrix = osg::Matrix::rotate(osg::PI_2, 1.0, 0.0, 0.0);
    yMt->setMatrix(yMatrix);

    osg::ShapeDrawable *yShape = new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(0.0f,0.0f,height/2),radius,height),hints);
    yShape->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
    osg::Geode *y = new osg::Geode;
    y->addDrawable(yShape);
    yMt->addChild(y);
}


void OSGRenderer::timerEvent(QTimerEvent *event)
{

    if(_applicationAboutToQuit)
    {
        return;
    }
    //qDebug()<<"OSGRenderer::timerEvent";
    // ask ViewerWidget to update 3D view
    if(getRunFrameScheme() != osgViewer::ViewerBase::ON_DEMAND ||
        checkNeedToDoFrame())
    {
        update();
    }
}

void OSGRenderer::setKeyboardModifiers(QInputEvent *event)
{
    unsigned int modkey = event->modifiers() & (Qt::ShiftModifier |
                                                Qt::ControlModifier |
                                                Qt::AltModifier);
    unsigned int mask = 0;

    if(modkey & Qt::ShiftModifier) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT;

    if(modkey & Qt::ControlModifier) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL;

    if(modkey & Qt::AltModifier) mask |= osgGA::GUIEventAdapter::MODKEY_ALT;

    m_osgWinEmb->getEventQueue()->getCurrentEventState()->setModKeyMask(mask);
}

osg::Node *OSGRenderer::createBase(const osg::Vec3d &center, float radius)
{
    int numTilesX = 50;
    int numTilesY = 50;

    float width = 2*radius;
    float height = 2*radius;

    osg::Vec3 v000(center - osg::Vec3(width*0.5f,height*0.5f,0.0f));
    osg::Vec3 dx(osg::Vec3(width/((float)numTilesX),0.0,0.0f));
    osg::Vec3 dy(osg::Vec3(0.0f,height/((float)numTilesY),0.0f));

    // fill in vertices for grid, note numTilesX+1 * numTilesY+1...
    osg::Vec3Array* coords = new osg::Vec3Array;
    int iy;
    for(iy=0;iy<=numTilesY;++iy)
    {
        for(int ix=0;ix<=numTilesX;++ix)
        {
            coords->push_back(v000+dx*(float)ix+dy*(float)iy);
        }
    }

    //Just two colours - black and white.
    osg::Vec4Array* colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,0.5f)); // white
    colors->push_back(osg::Vec4(0.2f,0.2f,0.2f,0.5f)); // black

    osg::ref_ptr<osg::DrawElementsUShort> whitePrimitives = new osg::DrawElementsUShort(GL_QUADS);
    osg::ref_ptr<osg::DrawElementsUShort> blackPrimitives = new osg::DrawElementsUShort(GL_QUADS);

    int numIndicesPerRow=numTilesX+1;
    for(iy=0;iy<numTilesY;++iy)
    {
        for(int ix=0;ix<numTilesX;++ix)
        {
            osg::DrawElementsUShort* primitives = ((iy+ix)%2==0) ? whitePrimitives.get() : blackPrimitives.get();
            //osg::DrawElementsUShort* primitives = whitePrimitives.get();
            primitives->push_back(ix    +(iy+1)*numIndicesPerRow);
            primitives->push_back(ix    +iy*numIndicesPerRow);
            primitives->push_back((ix+1)+iy*numIndicesPerRow);
            primitives->push_back((ix+1)+(iy+1)*numIndicesPerRow);
        }
    }

    // set up a single normal
    osg::Vec3Array* normals = new osg::Vec3Array;
    normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));

    osg::Geometry* geom = new osg::Geometry;
    geom->setVertexArray(coords);

    geom->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);

    geom->setNormalArray(normals, osg::Array::BIND_OVERALL);

    geom->addPrimitiveSet(whitePrimitives.get());
    geom->addPrimitiveSet(blackPrimitives.get());

    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(geom);

    return geode;
}

osg::Node *OSGRenderer::createGrid(double wd,double ht)
{
    int numTilesX = 50;
    int numTilesY = 50;

    float width = 2*wd;
    float height = 2*ht;
    osg::Vec3 center(0,0,0);
    double dx=width/numTilesX;
    double dy=height/numTilesY;
    osg::Vec4Array* colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,0.5f)); // white
    osg::Vec3Array* vertices = new osg::Vec3Array();
    int iy;
    for(iy=0;iy<=numTilesY;++iy)
    {
        double cy = (height*0.5f)-dy*iy;
        vertices->push_back(osg::Vec3(wd,cy,0));
        vertices->push_back(osg::Vec3(-wd,cy,0));
    }
    for(int ix=0;ix<=numTilesX;++ix)
    {
        double cx=(width*0.5f)-dx*ix;
        vertices->push_back(osg::Vec3(cx,ht,0));
        vertices->push_back(osg::Vec3(cx,-ht,0));
    }
    osg::Vec3Array* normals = new osg::Vec3Array;
    normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));

    osg::Geometry* geom = new osg::Geometry;
    geom->setVertexArray(vertices);

    geom->setColorArray(colors);
    osg::ref_ptr<osg::PrimitiveSet> primitiveSet = new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertices->size());
    geom->addPrimitiveSet(primitiveSet);

    geom->setNormalArray(normals, osg::Array::BIND_OVERALL);
    osg::ref_ptr<osg::LineWidth> lw = new osg::LineWidth(1.0);
    geom->getOrCreateStateSet()->setAttribute(lw, osg::StateAttribute::OFF);

    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(geom);

    return geode;
}

HUDAxis::HUDAxis()
{
    //this->addChild(osgDB::readNodeFile("axex.osgt"));
}

HUDAxis::HUDAxis(const HUDAxis &copy, osg::CopyOp copyOp):osg::Camera(copy,copyOp),_mainCamera(copy._mainCamera)
{

}

void HUDAxis::traverse(osg::NodeVisitor &nv)
{
    double fovy,aspectRatio,vNear,vFar;
    _mainCamera->getProjectionMatrixAsPerspective(fovy,aspectRatio,vNear,vFar);
    this->setProjectionMatrixAsOrtho2D(-10.0*aspectRatio,10*aspectRatio,-10,10);
    osg::Vec3 trans(8.5*aspectRatio,-8.5,-8.0);
    if(_mainCamera.valid()&& nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR)
    {
        osg::Matrix matrix=_mainCamera->getViewMatrix();
        matrix.setTrans(trans);
        this->setViewMatrix(matrix);
    }
    osg::Camera::traverse(nv);

}

HUDAxis::~HUDAxis()
{

}
